//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.guang.springbootexecutordistributedlock.distributedlock.db;

import java.util.UUID;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
import java.util.concurrent.atomic.AtomicInteger;

import com.guang.springbootexecutordistributedlock.distributedlock.DistributedLock;
import com.guang.springbootexecutordistributedlock.distributedlock.db.biz.DistributedLockRecordBiz;
import com.guang.springbootexecutordistributedlock.distributedlock.db.entity.DistributedLockMetaData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Component
public class DataBaseDistributedLock implements DistributedLock, InitializingBean, DisposableBean {
    private static final Logger LOGGER = LoggerFactory.getLogger(DataBaseDistributedLock.class);
    private final ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(10, new ThreadFactory() {
        private final String THREAD_PREFIX = "distributedLockScheduler_";
        private final AtomicInteger index = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, THREAD_PREFIX + this.index.getAndIncrement());
        }
    }, new AbortPolicy());
    private final ThreadLocal<DistributedLockMetaData> lockMetaDataThreadLocal = new ThreadLocal();
    private final long lockDuration = 3000L;
    private final long initialDelay = 2000L;
    private final long sleepMilliSeconds = 50L;
    @Value("${liguang.ip}")
    private String ip;
    @Value("${server.port}")
    private Integer port;
    @Autowired
    private DistributedLockRecordBiz distributedLockRecordBiz;

    public DataBaseDistributedLock() {
    }

    public void afterPropertiesSet() throws Exception {
        this.scheduler.setRemoveOnCancelPolicy(true);
    }

    public void lock(String lockKey) {
        if (!this.validateLockKey(lockKey)) {
            LOGGER.error("分布式锁lockKey不能为空");
            throw new IllegalArgumentException("lockKey不能为空");
        } else {
            while(true) {
                try {
                    if (this.tryLock(lockKey)) {
                        return;
                    }

                    this.sleep(50L);
                } catch (Throwable var3) {
                    this.sleep(50L);
                }
            }
        }
    }

    public boolean tryLock(String lockKey) {
        try {
            if (!this.validateLockKey(lockKey)) {
                LOGGER.error("分布式锁lockKey为空");
                return false;
            } else {
                DistributedLockMetaData metaData = this.lockMetaDataThreadLocal.get();
                if (metaData != null) {
                    if (this.distributedLockRecordBiz.isAlreadyHoldLock(metaData.getLockKey(), metaData.getUUID())) {
                        if (lockKey.equals(metaData.getLockKey())) {
                            LOGGER.info("lockKey:{}, ip:{}, port:{}, thread:{}, 该线程已获取分布式锁, 可重入成功", new Object[]{lockKey, this.ip, this.port, Thread.currentThread().getName()});
                            return true;
                        } else {
                            LOGGER.error("lockKey:{}, ip:{}, port:{}, thread:{}, 该线程已获取分布式锁, 可重入失败, 可能是数据库锁信息已超时或已被其他线程改变", new Object[]{lockKey, this.ip, this.port, Thread.currentThread().getName()});
                            return false;
                        }
                    } else {
                        LOGGER.error("分布式锁超时或不是该机器该线程获取, 获取锁失败");
                        if (!metaData.getFuture().isCancelled() && !metaData.getFuture().cancel(false)) {
                            LOGGER.error("lockKey:{}, ip:{}, port:{}, thread:{}, 该线程取消自动续时及删除锁信息失败, 需重试进行删除", new Object[]{lockKey, this.ip, this.port, Thread.currentThread().getName()});
                        } else {
                            this.lockMetaDataThreadLocal.remove();
                            LOGGER.info("lockKey:{}, ip:{}, port:{}, thread:{}, 该线程已取消自动续时及删除锁信息, 可重试获取锁", new Object[]{lockKey, this.ip, this.port, Thread.currentThread().getName()});
                        }

                        return false;
                    }
                } else {
                    String uuid = this.getUUID();
                    if (this.distributedLockRecordBiz.tryLock(lockKey, uuid, 3000L)) {
                        ScheduledFuture<?> future = this.scheduler.scheduleAtFixedRate(this.decorateTask(() -> {
                            this.refreshDistributedLock(lockKey, uuid, 3000L);
                        }), 2000L, 3000L, TimeUnit.MILLISECONDS);
                        this.lockMetaDataThreadLocal.set(this.buildDistributedLockMetaData(lockKey, uuid, future));
                        LOGGER.info("lockKey:{}, uuid:{}, ip:{}, port:{}, thread:{}, 已获取分布式锁", new Object[]{lockKey, uuid, this.ip, this.port, Thread.currentThread().getName()});
                        return true;
                    } else {
                        LOGGER.info("lockKey:{}, uuid:{}, ip:{}, port:{}, thread:{}, 竞争分布式锁失败, 可重试竞争", new Object[]{lockKey, uuid, this.ip, this.port, Thread.currentThread().getName()});
                        return false;
                    }
                }
            }
        } catch (Throwable var5) {
            LOGGER.error("lockKey:" + lockKey + ", ip:" + this.ip + ", port:" + this.port + ", thread:" + Thread.currentThread().getName() + ", 获取分布式锁失败", var5);
            return false;
        }
    }

    public boolean tryLock(String lockKey, long tryTimeOut, TimeUnit tryTimeUnit) {
        if (this.tryLock(lockKey)) {
            return true;
        } else {
            this.sleep(tryTimeUnit.toMillis(tryTimeOut));
            return this.tryLock(lockKey);
        }
    }

    public void unlock() {
        while(true) {
            try {
                DistributedLockMetaData metaData = (DistributedLockMetaData)this.lockMetaDataThreadLocal.get();
                if (metaData == null) {
                    LOGGER.info("ip:{}, port:{}, thread:{}, 该分布式锁信息已删除, 锁已释放", new Object[]{this.ip, this.port, Thread.currentThread().getName()});
                    return;
                }

                this.distributedLockRecordBiz.removeDistributedLockIfHoldLock(metaData.getLockKey(), metaData.getUUID());
                if (metaData.getFuture().isCancelled() || metaData.getFuture().cancel(false)) {
                    this.lockMetaDataThreadLocal.remove();
                    LOGGER.info("ip:{}, port:{}, thread:{}, 该分布式锁已释放", new Object[]{this.ip, this.port, Thread.currentThread().getName()});
                    return;
                }

                LOGGER.error("ip:{}, port:{}, thread:{}, 该分布式锁自动续时任务取消失败", new Object[]{this.ip, this.port, Thread.currentThread().getName()});
            } catch (Throwable var2) {
                this.sleep(50L);
            }
        }
    }

    public void destroy() throws Exception {
    }

    private Runnable decorateTask(Runnable task) {
        return () -> {
            try {
                task.run();
            } catch (Throwable var2) {
                LOGGER.error("延续分布式锁时长任务失败", var2);
            }

        };
    }

    private void sleep(long sleepMilliSeconds) {
        try {
            Thread.sleep(sleepMilliSeconds);
        } catch (Throwable var4) {
        }

    }

    private DistributedLockMetaData buildDistributedLockMetaData(String lockKey, String uuid, Future<?> future) {
        DistributedLockMetaData distributedLockMetaData = new DistributedLockMetaData();
        distributedLockMetaData.setLockKey(lockKey);
        distributedLockMetaData.setUUID(uuid);
        distributedLockMetaData.setIp(this.ip);
        distributedLockMetaData.setPort(this.port);
        distributedLockMetaData.setThreadName(Thread.currentThread().getName());
        distributedLockMetaData.setFuture(future);
        return distributedLockMetaData;
    }

    private String getUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    private boolean validateLockKey(String lockKey) {
        return StringUtils.hasText(lockKey);
    }

    private void refreshDistributedLock(String lockKey, String uuid, long lockDuration) {
        this.distributedLockRecordBiz.refreshDistributedLock(lockKey, uuid, lockDuration);
        LOGGER.info("lockKey:{}, uuid:{}, ip:{}, port:{}, thread:{}, 该分布式锁已自动续时成功", new Object[]{lockKey, uuid, this.ip, this.port, Thread.currentThread().getName()});
    }
}
